12 Python 面试题
1、FastAPI 和 Flask 有什么区别?
FastAPI 和 Flask 都是常用的、使用 Python 开发的 Web 框架,但它们使用的场景有所不同。
Flask 是一个更轻量级的 Web 框架,生态非常成熟,适合快速迭代开发小型项目,但是它主要支持同步 API,整体并发能力不如 FastAPI。
FastAPI 是一个基于 Starlette 和 Pydantic 构建的 Web 框架,它原生支持 ASGI 和异步编程。FastAPI 还天然支持 Swagger 文档,方便 API 联调,因此,在需要高并发、开箱即用的中大型项目选择 FastAPI 更加适合。
2、FastAPI 为什么适合 Agent 项目?
一个AI Agent应用通常有以下特点:
- IO 密集:一次请求可能涉及多个如 LLM 调用、
RAG检索、数据库查询、外部工具调用等操作。 - 结构化输入输出:在 Agent 应用的相关请求中,通常包含
message、session_id、权限数据等信息,需要结构化数据进行校验。 - 流式输出:LLM 生成答案往往耗时较长,为了保证用户体验,需要通过流式输出来返回结果。
- 依赖管理:需要对认证数据库、
Redis、向量数据库等依赖进行依赖注入。 - API 文档:Agent 应用需要对外提供 API 文档方便前端或者其他客户端调用。
因为 FastAPI 对这些需求支持比较好,所以 FastAPI 更适合 Agent 项目。
3、Python 异步编程如何实现?
在 Python 中,异步编程主要依赖 async、await 和 asyncio 事件循环。
在 Python 中,可以使用 async 来定义异步函数:
async def get_data(url: str):
await asyncio.sleep(2) # 模拟网络请求
return {"data": f"{url} 请求执行完成"}在调用异步函数时,代码不会立即执行,而是会返回一个协程对象。并且协程需要通过事件循环调度来执行。其中 await 关键字表示协程需要等待异步操作执行完成。在等待期间,事件循环可以调度其他协程。
async def main():
result = await get_data("https://api.test.com/v1/a")
print(result)
asyncio.run(main())除此之外,多个异步方法可以并发执行:
import asyncio
import datetime
async def get_data(url: str):
await asyncio.sleep(2) # 模拟网络请求
return {"data": f"{url} 请求执行完成"}
async def main():
await asyncio.gather(
get_data("https://api.test.com/v1/a"),
get_data("https://api.test.com/v1/b"),
get_data("https://api.test.com/v1/c")
)
print(f"开始时间:{datetime.datetime.now()}")
asyncio.run(main())
print(f"结束时间:{datetime.datetime.now()}")执行结果如下,三个异步方法调用 3 次,每次耗时 2s,但是使用异步编程就可以做到 2s 全部执行完成。

在 FastAPI 中,请求路由可以直接定义为 async def。框架内部会在 ASGI 运行环境中处理事件循环、调度请求。
4、asyncio 原理是什么?
asyncio 的核心实现机制是事件循环(Event Loop)。事件循环本质上可以理解为一个任务调度器,它在内部维护了一组协程任务,当某个任务遇到 await 需要等待 IO 时,事件循环就将这个任务挂起,切换去执行其他任务,等 IO 结束后,再恢复原来的任务执行。
asyncio 需要协程在 await 处主动让出控制权,事件循环才能调度执行其他任务,因此在异步方法中,不要直接执行阻塞操作。比如,在协程中用 time.sleep(10) 会阻塞整个事件循环,会导致其他任务也无法继续处理。所以应该使用 asyncio.sleep,或者把阻塞任务放到线程池中执行。
5、协程和线程有什么区别?
协程和线程都可以实现异步任务,但两者的调度方式、资源消耗、使用场景有所不同。
- 调度方式
线程由操作系统调度,线程的切换是抢占式的。多个线程可以分配到不同 CPU 核心执行,但 CPython 中受 GIL(Global Interpreter Lock,全局解释器锁)影响,同一时刻只能有一个线程执行 Python 字节码,因此在处理 CPU 密集型任务时,无法通过多线程达到理想的效果。
协程是由事件循环进行调度的,任务间切换是协作式的。协程只有在遇到 await 时,才主动让出控制权,继续执行其他协程。
- 资源开销
线程需要操作系统分配栈空间,创建和切换线程的成本较高。而协程更轻量,创建和切换成本较低。IO 密集型任务较多的系统,可以使用大量的协程任务。
- 适用场景
线程适合并发数量不高、使用同步库(如 requests)或不方便改造成异步的 IO 密集型等场景。由于 CPython 存在 GIL,所以线程不适合 CPU 密集型计算。
协程更适合网络请求、LLM 调用、访问数据库、访问 Redis、调用第三方 API 等 IO 密集任务。
- 编程复杂度
多线程开发需要关注锁、竞争条件、死锁等问题,使用起来比较复杂,而协程在单线程事件循环中运行,通过 await 主动让出执行权,一个线程就可以管理成百上千个并发任务。
6、WebSocket 和 SSE 区别是什么?
WebSocket 和 SSE 都可以实现服务器实时推送数据,但两者的通信模型不同。
SSE(Server-Sent Events) 本身是基于 HTTP 协议实现的,SSE 是服务器到客户端的单向推送。客户端通过一个普通 HTTP 请求建立连接,服务器可以持续返回数据,因此,非常适合 LLM 流式输出、日志推送、任务进度等场景。它实现简单,但是客户端不能主动发送数据给服务器端。
WebSocket 则是在 HTTP 握手完成后,升级为 WebSocket 协议,并建立了一个全双工的通信通道。连接建立之后,客户端和服务器端都可以随时主动发送消息,适用于在线聊天、实时游戏等需要频繁双向通信的场景。
在 AI 应用中,绝大多数场景都是将 LLM 生成的结果持续地推送给客户端,所以通常使用 SSE;如果 Agent 应用运行过程中需要客户端实时发送数据、确认操作等双向交互,则更适合使用 WebSocket。